package com.kly.manager.websocket;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.kly.domain.entity.User;
import com.kly.manager.websocket.disruptor.PictureEditEventProducer;
import com.kly.manager.websocket.enums.PictureEditActionEnum;
import com.kly.manager.websocket.enums.PictureEditMessageTypeEnum;
import com.kly.manager.websocket.model.PictureEditMessageRequest;
import com.kly.manager.websocket.model.PictureEditMessageResponse;
import com.kly.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author admin
 * @date 2025/3/21
 */
@Configuration
@Slf4j
public class PictureEditHandler extends TextWebSocketHandler {

    @Resource
    private UserService userService;
    @Resource
    @Lazy
    private PictureEditEventProducer pictureEditEventProducer;
    /**
     * 用户编辑图片userId
     */
    private final Map<Long, Long> pictureEditUsers = new ConcurrentHashMap<>();

    /**
     * 用户编辑图片session
     */
    private final Map<Long, Set<WebSocketSession>> pictureEditSessions = new ConcurrentHashMap<>();


    /**
     * 连接建立成功
     *
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //     获取信息
        Long pictureId = (long) session.getAttributes().get("pictureId");
        User user = (User) session.getAttributes().get("loginUser");


        pictureEditSessions.putIfAbsent(pictureId, ConcurrentHashMap.newKeySet());
        pictureEditSessions.get(pictureId).add(session);

        //     构造消息
        PictureEditMessageResponse pictureEditMessageResponse = new PictureEditMessageResponse();
        //     设置类型
        pictureEditMessageResponse.setType(PictureEditMessageTypeEnum.INFO.getValue());
        //     设置消息
        String message = String.format("用户%s加入编辑图片", user.getUserName());
        pictureEditMessageResponse.setMessage(message);
        //     设置用户信息
        pictureEditMessageResponse.setUserVO(userService.getUserVo(user));
        // 广播信息
        broadcastToPicture(pictureId, pictureEditMessageResponse);
    }


    /**
     * 处理消息
     *
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //     解析TextMessage
        PictureEditMessageRequest pictureEditMessageRequest = JSONUtil.toBean(message.getPayload(), PictureEditMessageRequest.class);
        //     获取信息
        Long pictureId = (long) session.getAttributes().get("pictureId");
        User user = (User) session.getAttributes().get("loginUser");
        //     选择消息类型
        pictureEditEventProducer.publishEvent(pictureEditMessageRequest, session, pictureId, user);

    }

    /**
     * 处理编辑操作
     *
     * @param pictureEditMessageRequest
     * @param session
     * @param pictureId
     * @param user
     * @throws IOException
     */
    public void handleEditActionMessage(PictureEditMessageRequest pictureEditMessageRequest, WebSocketSession session, Long pictureId, User user) throws IOException {
        Long userId = pictureEditUsers.get(pictureId);
        String editAction = pictureEditMessageRequest.getEditAction();
        PictureEditActionEnum pictureEditActionEnum = PictureEditActionEnum.getEditActionEnumByValue(editAction);
        if (pictureEditActionEnum == null) {
            log.error("编辑图片操作错误");
            return;
        }
        if (user != null && userId.equals(user.getId())) {
            //     确认是编辑者
            PictureEditMessageResponse pictureEditMessageResponse = new PictureEditMessageResponse();
            pictureEditMessageResponse.setType(PictureEditMessageTypeEnum.EDIT_ACTION.getValue());
            pictureEditMessageResponse.setEditAction(editAction);
            pictureEditMessageResponse.setUserVO(userService.getUserVo(user));
            String message = String.format("用户%s执行了%s操作", user.getUserName(), editAction);
            pictureEditMessageResponse.setMessage(message);
            //     发布消息 除自己以外 否则重复编辑
            broadcastToPicture(pictureId, pictureEditMessageResponse, session);
        }
    }

    /**
     * 处理退出编辑
     *
     * @param pictureId
     * @param user
     */
    public void handleEditExitMessage(Long pictureId, User user) throws IOException {
        // 获取编辑者id
        Long userId = pictureEditUsers.get(pictureId);
        if (userId != null && userId.equals(user.getId())) {
            pictureEditUsers.remove(pictureId);
            //     构造消息
            PictureEditMessageResponse pictureEditMessageResponse = new PictureEditMessageResponse();
            pictureEditMessageResponse.setType(PictureEditMessageTypeEnum.EXIT_EDIT.getValue());
            String message = String.format("用户%s退出编辑图片", user.getUserName());
            pictureEditMessageResponse.setMessage(message);
            pictureEditMessageResponse.setUserVO(userService.getUserVo(user));
            broadcastToPicture(pictureId, pictureEditMessageResponse);
        }
    }

    /**
     * 处理进入编辑
     *
     * @param pictureId
     * @param user
     * @throws IOException
     */
    public void handleEditEnterMessage(Long pictureId, User user) throws IOException {
        //     如果没有用户编辑才进行
        if (!pictureEditUsers.containsKey(pictureId)) {
            pictureEditUsers.putIfAbsent(pictureId, user.getId());

            //     构造消息
            PictureEditMessageResponse pictureEditMessageResponse = new PictureEditMessageResponse();
            pictureEditMessageResponse.setType(PictureEditMessageTypeEnum.ENTER_EDIT.getValue());
            String message = String.format("用户%s编辑图片", user.getUserName());
            pictureEditMessageResponse.setMessage(message);
            pictureEditMessageResponse.setUserVO(userService.getUserVo(user));

            broadcastToPicture(pictureId, pictureEditMessageResponse);
        }
    }

    /**
     * 广播图片编辑消息
     *
     * @param pictureId
     * @param messageResponse
     * @param excludeSession
     * @throws IOException
     */
    public void broadcastToPicture(Long pictureId, PictureEditMessageResponse messageResponse, WebSocketSession excludeSession) throws IOException {
        //     获取编辑图片的session
        Set<WebSocketSession> webSocketSessions = pictureEditSessions.get(pictureId);
        if (CollUtil.isEmpty(webSocketSessions)) {
            return;
        }
        //     发送消息 精度损失问题 Long
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(module);
        //     转换
        String message = objectMapper.writeValueAsString(messageResponse);
        TextMessage textMessage = new TextMessage(message);
        for (WebSocketSession webSocketSession : webSocketSessions) {
            if (webSocketSession.equals(excludeSession)) {
                continue;
            }

            if (webSocketSession.isOpen()) {
                webSocketSession.sendMessage(textMessage);
            }
        }
    }


    /**
     * 关闭连接后操作
     *
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //     获取信息
        Long pictureId = (long) session.getAttributes().get("pictureId");
        User user = (User) session.getAttributes().get("loginUser");

        //     移除当前用户编辑态
        handleEditExitMessage(pictureId, user);

        //     删除会话
        Set<WebSocketSession> webSocketSessions = pictureEditSessions.get(pictureId);
        if (CollUtil.isNotEmpty(webSocketSessions)) {
            webSocketSessions.remove(session);
            if (CollUtil.isEmpty(webSocketSessions)) {
                pictureEditSessions.remove(pictureId);
            }
        }

        //     响应
        PictureEditMessageResponse pictureEditMessageResponse = new PictureEditMessageResponse();
        pictureEditMessageResponse.setType(PictureEditMessageTypeEnum.INFO.getValue());
        String message = String.format("用户%s退出编辑图片", user.getUserName());
        pictureEditMessageResponse.setMessage(message);
        pictureEditMessageResponse.setUserVO(userService.getUserVo(user));
        broadcastToPicture(pictureId, pictureEditMessageResponse);
    }

    /**
     * 广播图片编辑消息
     *
     * @param pictureId
     * @param messageResponse
     * @throws IOException
     */
    public void broadcastToPicture(Long pictureId, PictureEditMessageResponse messageResponse) throws IOException {
        broadcastToPicture(pictureId, messageResponse, null);
    }
}
